package com.zjpavt.client.job;

import com.zjpavt.client.job.util.TriggerUtil;
import com.zjpavt.client.util.LoggerUtil;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

/**
 * 所有发布的job的管理类
 *
 * @author gxy
 */
public class JobManager {
    private static final Logger log = LoggerFactory.getLogger(JobManager.class);
    private static final JobManager JOB_MANAGER = new JobManager();

    //这两个job当来了更高级级别指令是而取消job
    /**
     * 不带有效时长的k指令
     */
    static final String TIMING_JOB_TYPE_K_NOTWITHTIME_COMMAND = "kNotWithTimeCommand";
    /**
     * 切换到定时器指令
     */
    static final String TIMING_JOB_TYPE_SWITCH_TO_TIMER_COMMAND = "SwitchToTimerCommand";
    /**
     * 刷新定时器指令
     */

    static final String TIMING_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND = "RefreshTodayTimerCommand";
    /**
     * 关闭定时器指令
     */

    static final String TIMING_JOB_TYPE_CLOSE_TODAY_TIMER_COMMAND = "CloseTodayTimerCommand";


    /**
     * 刷新离线指令(执行离线指令的job)
     */
    static final String TIMING_JOB_TYPE_REFRESH_OFFLINE_COMMAND = "RefreshOfflineCommand";

    /**
     * 0点刷新离线指令任务
     */
    static final String DAILY_JOB_TYPE_REFRESH_OFFLINE_COMMAND = "DailyRefreshOfflineCommand";

    /**
     * 0点刷新当天定时指令任务
     */
    static final String DAILY_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND = "TodayTimerCommand";


    public static JobManager getInstance() {
        return JOB_MANAGER;
    }

    private final HashMap<String, JobBuilder> jobMap = new HashMap<>();

    private Scheduler jobScheduler = null;

    /**
     * 共享资源
     */
    private final Set<TriggerKey> mTodayTimerTriggerKeySet = new HashSet<>();
    private final Set<TriggerKey> mOfflineCommandTriggerKeySet = new HashSet<>();
    private volatile TriggerKey mTempTriggerKey;

    private JobManager() {
        initJobMap();
        try {
            jobScheduler = new StdSchedulerFactory().getScheduler();
            jobScheduler.start();
            initDailyDayJob();
        } catch (SchedulerException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
    }

    /**
     * job的重复利用需要每个job重新添加一个jobKey（可改进）
     */
    private void initJobMap() {
        //tempJob
        jobMap.put(TIMING_JOB_TYPE_K_NOTWITHTIME_COMMAND, JobBuilder.newJob(TimingJob.class)
                   .usingJobData("TYPE", TIMING_JOB_TYPE_K_NOTWITHTIME_COMMAND)
                  );
        jobMap.put(TIMING_JOB_TYPE_SWITCH_TO_TIMER_COMMAND, JobBuilder.newJob(TimingJob.class)
                   .usingJobData("TYPE", TIMING_JOB_TYPE_SWITCH_TO_TIMER_COMMAND)
                  );
        //当天定时器指令开启与关闭job
        jobMap.put(TIMING_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND, JobBuilder.newJob(TimingJob.class)
                   .usingJobData("TYPE", TIMING_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND)
                  );
        jobMap.put(TIMING_JOB_TYPE_CLOSE_TODAY_TIMER_COMMAND, JobBuilder.newJob(TimingJob.class)
                   .usingJobData("TYPE", TIMING_JOB_TYPE_CLOSE_TODAY_TIMER_COMMAND)
                  );
        //离线指令执行指令
        jobMap.put(TIMING_JOB_TYPE_REFRESH_OFFLINE_COMMAND, JobBuilder.newJob(TimingJob.class)
                   .usingJobData("TYPE", TIMING_JOB_TYPE_REFRESH_OFFLINE_COMMAND)
                  );
        //每天0点发布的定时器指令与离线指令
        jobMap.put(DAILY_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND, JobBuilder.newJob(DailyJob.class)
                   .usingJobData("TYPE", DAILY_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND)
                  );
        jobMap.put(DAILY_JOB_TYPE_REFRESH_OFFLINE_COMMAND, JobBuilder.newJob(DailyJob.class)
                   .usingJobData("TYPE", DAILY_JOB_TYPE_REFRESH_OFFLINE_COMMAND)
                  );
    }

    /**
     * 初始化0点执行的发布离线指令任务与定时器任务
     * 该任务是发布离线指令以及定时器指令的任务，而不是执行任务
     */
    public void initDailyDayJob() throws SchedulerException {
        //由于发布零点任务后可能由于时间变化而导致任务执行不了，所以在修改时间后去取消之前发布的任务，并重新发布
        //判断两个jobKey是否相同是判断他们的name跟group是否相同，我这里只是命令了name所以当name相同时就是同一个JobJek。
        jobScheduler.deleteJob(new JobKey(DAILY_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND));
        jobScheduler.deleteJob(new JobKey(DAILY_JOB_TYPE_REFRESH_OFFLINE_COMMAND));
        jobScheduler.scheduleJob(jobMap.get(DAILY_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND).withIdentity(DAILY_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND).build(), TriggerUtil.getDailyTrigger(0, 0, 0));
        jobScheduler.scheduleJob(jobMap.get(DAILY_JOB_TYPE_REFRESH_OFFLINE_COMMAND).withIdentity(DAILY_JOB_TYPE_REFRESH_OFFLINE_COMMAND).build(), TriggerUtil.getDailyTrigger(0, 0, 0));
    }

    /**
     * 由K指令发布的job，跟光照强度的job
     */
    public void addTempJobTriggerAndExec(String jobName, Trigger jobTrigger) {
        if (!checkJobDefaultName(jobName)) {
            return;
        }
        cancelTempJob();
        try {
            jobScheduler.scheduleJob(jobMap.get(jobName).withIdentity(UUID.randomUUID().toString()).build(), jobTrigger);
            mTempTriggerKey = jobTrigger.getKey();
        } catch (SchedulerException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
    }

    /**
     * 当天定时器job
     */
    public void addTodayTriggerAndExec(String jobName, Trigger jobTrigger) {
        if (!checkTodayTimerJobName(jobName)) {
            return;
        }
        saveAndExecTriggerKey(jobName, mTodayTimerTriggerKeySet, jobTrigger);
    }

    /**
     * 添加离线指令job
     */
    public void addOfflineCommandTriggerAndExec(Trigger jobTrigger) {
        saveAndExecTriggerKey(TIMING_JOB_TYPE_REFRESH_OFFLINE_COMMAND, mOfflineCommandTriggerKeySet, jobTrigger);
    }

    private void saveAndExecTriggerKey(String jobName, final Set<TriggerKey> triggerKeys, Trigger trigger) {
        try {
            jobScheduler.scheduleJob(jobMap.get(jobName).withIdentity(UUID.randomUUID().toString()).build(), trigger);
            synchronized (triggerKeys) {
                triggerKeys.add(trigger.getKey());
            }
        } catch (SchedulerException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
    }

    /**
     * 用于K指令发布的job，跟光照强度的job下一个发布前取消上一个job
     */
    public void cancelTempJob() {
        if (mTempTriggerKey == null) {
            return;
        }
        try {
            jobScheduler.pauseTrigger(mTempTriggerKey);
        } catch (SchedulerException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
    }

    /**
     * 取消当天定时的job
     */

    public void cancelTodayTimerJob() {
        cancelJobs(mTodayTimerTriggerKeySet);
    }

    /**
     * 取消离线指令的job
     */

    public void cancelOfflineCommandJob() {
        cancelJobs(mOfflineCommandTriggerKeySet);
    }

    private void cancelJobs(final Set<TriggerKey> triggerKeys) {
        if (triggerKeys == null || triggerKeys.isEmpty()) {
            return;
        }
        Set<TriggerKey> copySet;
        synchronized (triggerKeys) {
            copySet = new HashSet<>(triggerKeys);
            triggerKeys.clear();
        }

        for (TriggerKey key : copySet) {
            try {
                jobScheduler.pauseTrigger(key);
            } catch (SchedulerException e) {
                log.error(LoggerUtil.convertException2String(e));
            }
        }
    }

    private boolean checkJobDefaultName(String jobName) {
        if (jobName == null) {
            return false;
        }
        switch (jobName) {
        case TIMING_JOB_TYPE_K_NOTWITHTIME_COMMAND:
            return true;
        case TIMING_JOB_TYPE_SWITCH_TO_TIMER_COMMAND:
            return true;
        default:
            return false;
        }
    }

    private boolean checkTodayTimerJobName(String jobName) {
        if (jobName == null) {
            return false;
        }
        switch (jobName) {
        case TIMING_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND:
            return true;
        case TIMING_JOB_TYPE_CLOSE_TODAY_TIMER_COMMAND:
            return true;
        default:
            return false;
        }
    }
}
